package com.chat.config;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.chat.domain.ChatAccount;
import com.chat.domain.User;
import com.chat.filter.JWTCalibrationFilter;
import com.chat.mapper.ChatAccountMapper;
import com.chat.service.impl.AuthenticationServiceImpl;
import com.chat.utils.JwtUtil;
import com.chat.utils.RedisUtil;
import com.chat.response.ChatAccountResponse;
import com.chat.response.BaseResponse;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.sql.DataSource;
import java.io.IOException;

/**
 * Security配置类
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Resource
    AuthenticationServiceImpl authenticationService;
    @Resource
    private DataSource           dataSource;
    @Resource
    private JWTCalibrationFilter jwtCalibrationFilter;
    @Resource
    private JwtUtil              jwtUtil;

    //配置密码的加密方式
    @Bean
    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();
    }


    //设置记住密码的时候，token保存在一个表里面
    //这里使用JDBC来进行创建
    @Bean
    protected PersistentTokenRepository tokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        jdbcTokenRepository.setCreateTableOnStartup(false);
        return jdbcTokenRepository;
    }

    //设置AuthenticationManager，从中获取Authentication对象，并且重写userDetailsService的方法
    @Bean
    public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(authenticationService);
        authenticationProvider.setPasswordEncoder(passwordEncoder);
        return new ProviderManager(authenticationProvider);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http,PersistentTokenRepository tokenRepository) throws Exception {
        http
                .csrf(AbstractHttpConfigurer::disable) // 禁用CSRF
                .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 配置CORS
                .authorizeHttpRequests(authz -> authz
                        .requestMatchers("/**").permitAll() // 配置URL不需要登录就可以访问
                        .anyRequest().authenticated()) // 其他请求需要认证
                .formLogin(form -> form
                        .loginProcessingUrl("/cat/auth/login") // 配置登录URL
                        .successHandler(this::onAuthenticationSuccess) // 登录成功处理
                        .failureHandler(this::onAuthenticationFailure)) // 登录失败处理
                .logout(logout -> logout
                        .logoutUrl("/cat/auth/logout"))// 配置登出URL)
                .rememberMe(rememberMe -> rememberMe
                        .rememberMeParameter("remember") // 配置记住我参数
                        .tokenRepository(tokenRepository) // 配置持久化令牌
                        .tokenValiditySeconds(3600 * 24 * 7)) // 令牌有效期
                .addFilterBefore(jwtCalibrationFilter, UsernamePasswordAuthenticationFilter.class); // JWT校验过滤器

        return http.build();
    }



    private CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration cors = new CorsConfiguration();
        cors.addAllowedOriginPattern("*");
        cors.setAllowCredentials(true);
        cors.addAllowedMethod("*");
        cors.addExposedHeader("*");
        cors.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", cors);
        return source;
    }

    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        String remember = request.getParameter("remember");
        response.setContentType("application/json;charset=utf-8");
        User user = (User) authentication.getPrincipal();
        ChatAccount chatAccount = user.getChatAccount();

        String token = jwtUtil.createToken(chatAccount, "true".equals(remember));
        ChatAccountResponse bean = BeanUtil.toBean(chatAccount, ChatAccountResponse.class);
        bean.setJWT(token);
        response.getWriter().write(JSONObject.toJSONString(BaseResponse.success("登陆成功", bean)));
    }


    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(JSONObject.toJSONString(BaseResponse.failure(401, exception.getMessage())));
    }
}


